package com.itheima;

public class MySQL_And_MyBatis笔记 {
/*
 --------------------------------------------------------------------------------
                                 <<MySQL笔记>>
 --------------------------------------------------------------------------------
    启动mysql服务 net start mysql
    停止mysql服务 net stop mysql
    连接MySQL数据库(本机/远端) mysql  -u用户名   -p密码   [ -h数据库服务器IP地址   -P端口号 ]
    */

    /*
    --------------------------------------------------------------------------------
    关系型数据库（RDBMS）: 建立在关系模型基础上，由多张相互连接的二维表组成的数据库。

    SQL(Structured Query Language)结构化查询语言：一门操作关系型数据库的编程语言，定义操作所有关系型数据库的统一标准。
    1.SQL语句可以单行或多行书写，以分号结尾。
    2.SQL语句可以使用空格/缩进来增强语句的可读性。
    3.MySQL数据库的SQL语句不区分大小写。
    注释：
    1.单行注释：-- 注释内容 或 # 注释内容(MySQL特有)
    2.多行注释： /* 注释内容 *\/

    SQL语句通常被分为四大类：
    1.DDL	Data Definition Language	数据定义语言，用来定义数据库对象(数据库，表，字段)
    2.DML	Data Manipulation Language	数据操作语言，用来对数据库表中的数据进行增删改
    3.DQL	Data Query Language	数据查询语言，用来查询数据库中表的记录
    4.DCL	Data Control Language	数据控制语言，用来创建数据库用户、控制数据库的访问权限
    */

    /*
    --------------------------------------------------------------------------------
    DDL 英文全称是 Data Definition Language，数据定义语言，用来定义数据库对象(数据库、表)。

    查询所有数据库：show databases;
    查询当前数据库：select database();
    使用(打开)数据库：use  数据库名 ;
    创建数据库：create  database [ if  not  exists ] 数据库名 ;
    删除数据库：drop  database [ if exists ]   数据库名 ;
    上述语法中的database，也可以替换成 schema。如：create schema db01;


    --------------------------------------------------------------------------------
    新建表:
    create table  表名(
    字段1  字段1类型 [约束]  [comment  字段1注释 ],
    字段2  字段2类型 [约束]  [comment  字段2注释 ],
    ......
    字段n  字段n类型 [约束]  [comment  字段n注释 ]
    ) [ comment  表注释 ] ;

    约束 --- 约束是作用于表中字段上的规则，用于限制存储在表中的数据,可以在创建表/修改表的时候添加约束,来保证数据库中数据的正确性,有效性和完整性
    非空约束 not null --- 限制该字段值不能为null
    唯一约束 unique --- 保证字段的所有数据都是唯一、不重复的
    默认约束 default --- 保存数据时，如果未指定该字段值，则采用默认值
    主键约束 primary key --- 主键是一行数据的唯一标识，要求非空且唯一
    外键约束 foreign key --- 让两张表的数据建立连接，保证数据的一致性和完整性

    MySQL中的数据类型有很多,主要分为三类:数值类型,字符串类型,日期时间类型
    整数用int,
    浮点数用double,
    字符串用char(定长字符串),varchar(可变长字符串),(例如char(10)最多只能存10个字符,不足10个字符占用10个字符空间,而varchar(10)最多只能存10个字符,不足10个字符,将会按照实际长度存储),
    日期类型date(YYYY-MM-DD日期值),datetime(YYYY-MM-DD HH:MM:SS日期和时间值)

    例如:
    create table student(
    id int unique comment "id为唯一标识",
    username varchar(32) not null unique,
    name varchar(32) not null,
    age int,
    gender char(1) default "男"
    )comment "DDL入门案例,定义student表";

    --------------------------------------------------------------------------------
    DDL表查询操作

    查询当前数据库所有表：show tables;

    查询建表语句：show create table 表名;
    show create table student;
    CREATE TABLE `student` (
    `id` int(11) DEFAULT NULL COMMENT 'id为唯一标识',
    `username` varchar(32) NOT NULL,
    `name` varchar(32) NOT NULL,
    `age` int(11) DEFAULT NULL,
    `gender` char(1) DEFAULT '男' COMMENT '这里是说明',
    UNIQUE KEY `username` (`username`),
    UNIQUE KEY `id` (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='DDL入门案例,定义student表';

    SQL中一对反引号``之间的内容会被当成纯文本,因此可以用`关键字`如`create`来作为表名建表


    查询表结构(描述表的字段)：desc  表名;

    --------------------------------------------------------------------------------
    其他DDL表操作

    添加字段：alter table 表名  add  字段名  类型(长度)  [comment  注释]  [约束];
    修改字段类型：alter table 表名 modify  字段名  新数据类型(长度);
    修改字段名和字段类型：alter table 表名 change  旧字段名  新字段名  类型 (长度)  [comment 注释]  [约束];
    删除字段：alter table 表名 drop  column 字段名;
    修改表名： rename table 表名 to  新表名;
    删除表：drop table [ if exists ] 表名;
    */

    /*
    --------------------------------------------------------------------------------
    DML英文全称是Data Manipulation Language(数据操作语言)，用来对数据库中表的数据记录进行增、删、改操作。


    插入数据

    指定字段添加数据：insert into 表名 (字段名1, 字段名2)  values (值1, 值2);
    全部字段添加数据：insert into 表名 values (值1, 值2, ...);


    注意: 插入数据时，指定的字段顺序需要与值的顺序是一一对应的。
    字符串和日期型数据应该包含在引号(单双都可以)中。
    插入的数据大小，应该在字段的规定范围内。
    在数据库中对比日期时,日期对应的毫秒值数值越大批量添加数据（指定字段）：insert into 表名 (字段名1, 字段名2)  values (值1, 值2), (值1, 值2);
    批量添加数据（全部字段）：insert into 表名 values (值1, 值2, ...), (值1, 值2, ...);则日期越大

    --------------------------------------------------------------------------------
    修改数据

    修改数据：update  表名  set  字段名1 = 值1 , 字段名2 = 值2 , .... [ where  条件];

    注意:修改语句的条件可以有，也可以没有，如果没有条件，则会修改整张表的所有数据。
    --------------------------------------------------------------------------------
    删除数据

    删除数据：delete  from  表名  [ where  条件 ];

    DELETE 语句的条件可以有，也可以没有，如果没有条件，则会删除整张表的所有数据。
    DELETE 语句不能只删除某一个字段的值,每次一删就是一行(如果要将某个字段置为空，可以使用UPDATE语句，将该字段的值置为NULL)。
    --------------------------------------------------------------------------------
    查询数据
    DQL英文全称是Data Query Language(数据查询语言)，用来查询数据库表中的记录,日常使用数据库做的最多的操作就是查询数据

    完整查询:
    select 字段列表
    from 表名列表
    where 条件列表
    group by 分组字段列表
    having 分组后条件列表
    order by 排序字段列表
    limit 分页参数

    查询多个字段：select  字段1, 字段2, 字段3  from   表名;

    查询所有字段（通配符）：select  *  from   表名;
    * 号代表查询所有字段，在实际开发中尽量少用（不直观、影响效率）。

    给字段设置别名：select  字段1  [ as  别名1 ] , 字段2  [ as  别名2 ]   from   表名;
    如:select id as 编号,username as 用户名,name as 姓名,age as 年龄,gender as 性别 from student;
    as可以省略,别名只改变显示效果,不改变数据库内容
    还可以给表设置别名:select s.id as 编号,s.username as 用户名,s.name as 姓名,s.age as 年龄,s.gender as 性别 from student as s;

    去除重复记录：select  distinct  字段列表  from   表名;
    当字段列表有多个字段时,需要两行数据这些字段全部相同才算重复


    --------------------------------------------------------------------------------
    条件查询:
    select 字段列表 from 表名列表 where 条件列表
    比较运算符      功能
    >               大于
    >=              大于等于
    <               小于
    <=              小于等于
    =               等于
    <>或!=          不等于
    between...and...在某个范围之内(含最小、最大值)
    in(...)         在in之后的列表中的值，多选一
    like  占位符    模糊匹配( _ 匹配单个字符, % 匹配任意个字符)
    is null         是null

    逻辑运算符      功能
    and或&&         并且 (多个条件同时成立)
    or或||          或者 (多个条件任意一个成立)
    not或!          非 , 不是

    [----------------------------------------------
    1.查询id大于10的所有数据
    select * from tb_emp where id>10;
    2.查询所有男性
    select * from tb_emp where gender = 1;
    select * from tb_emp where gender != 2;
    select * from tb_emp where gender <> 2;
    3.查询2011-2015年的数据
    select * from tb_emp where entrydate >= "2011-01-01" && entrydate < "2016-01-01";
    select * from tb_emp where entrydate >= "2011-01-01" and entrydate < "2016-01-01";
    select * from tb_emp where entrydate BETWEEN "2011-01-01" and "2015-12-31";
    4.请查询出所有的主管
    select * from tb_emp where job = 3 || job = 4;
    select * from tb_emp where job in (3,4);
    5.查询出有职位的人
    select * from tb_emp where job != null; -- 不能使用!=判空,查不出数据
    select * from tb_emp where job is not null; -- is null 判空 is not null 判非空
    6.查询出姓张的人
    select * from tb_emp where name like '张无忌';
    select * from tb_emp where name like '张%';
    select * from tb_emp where name like '张_';-- 姓张且两个字
    select * from tb_emp where name not like '%张%';-- 名字中没有张 not == !
    ----------------------------------------------]

    --------------------------------------------------------------------------------
    分组查询 前置知识 聚合函数
    介绍：将一列数据作为一个整体，进行纵向计算。
    语法：select  聚合函数(字段列表)  from   表名 ;
    函数       功能
    count      统计数量
    max        最大值
    min        最小值
    avg        平均值
    sum        求和
    注意: null值不参与所有聚合函数运算,例如使用某字段count计数时,null值所在行将不算数
    统计数量可以使用：count(*)   count(字段)   count(常量)，推荐使用count(*)。

    [----------------------------------------------
    1.求一共有多少条记录?
    select COUNT(*) from tb_emp;
    select COUNT(0) from tb_emp;
    2.求id最大的人
    select max(id) as id最大的人 from tb_emp;
    3.求id的平均值
    select avg(id) from tb_emp;
    4.求id的和
    select sum(id) from tb_emp;
    5.求平均入职时间
    select avg(entrydate) from tb_emp;
    ----------------------------------------------]

    --------------------------------------------------------------------------------
    分组查询:
    select 字段列表 from 表名列表 where 条件列表 group by 分组字段列表 having 分组后条件列表

    where与having区别:
    执行时机不同：where是分组之前进行对每一条数据过滤，不满足where条件，不参与分组；而having是分组之后对每一组结果数据进行过滤。
    判断条件不同：where不能对聚合函数进行判断，而having可以。
    注意:
    分组之后，查询的字段一般为聚合函数和分组字段，查询其他字段无任何意义。
    执行顺序: where  >  聚合函数 > having 。

    [----------------------------------------------
    1.求男生的最晚入职时间
    select max(entrydate)  from tb_emp as te where gender=1
    2.求女生的最晚入职时间
    select max(entrydate)  from tb_emp as te where gender=2
    3.在一次查询中,分别查询出各种性别的最晚入职
    时间
    select gender,max(entrydate) from tb_emp group by gender;
    4.求不同性别的人数
    select gender as 性别,count(*) as 人数 from tb_emp GROUP BY gender;
    5.求不同性别的人数,2010前入职的人不计入在内
    select gender as 性别,count(*) as 人数 from tb_emp where entrydate>="2010-01-01" GROUP BY gender;
    6.求不同性别的人数,2010前入职的人不计入在内,如果性别分组少于3人则不显示
    select gender as 性别,count(*) as 人数 from tb_emp where entrydate>="2010-01-01" GROUP BY gender having 人数 >= 3;
    ----------------------------------------------]

    --------------------------------------------------------------------------------
    排序查询:order by
    select  字段列表  from   表名   [ where   条件列表 ] [ group by  分组字段 ] order  by  字段1  排序方式1 , 字段2  排序方式2 … ;
    排序方式:
    ASC：升序（默认值）
    DESC：降序
    如果是多字段排序，当第一个字段值相同时，才会根据第二个字段进行排序。

    select * from 表名 order by 字段名1 desc; -- 按照字段1降序排序
    select * from 表名 order by 字段名2 asc, 字段名3 desc; -- 优先按照字段2升序排序,相同时再按照字段3降序排序

    --------------------------------------------------------------------------------
    分页查询:limit结果限制关键字(通常用于分页操作)
    select  字段列表  from   表名  limit  起始索引, 查询记录数 ;

    select * from tableName limit (pageNum-1)*pageSize,pageSize;

    注意:
    1.起始索引从0开始，起始索引 = （查询页码pageNum - 1）* 每页显示记录数pageSize。
    2.分页查询是数据库的方言，不同的数据库有不同的实现，MySQL中是LIMIT。
    3.如果查询的是第一页数据，起始索引可以省略，直接简写为 limit 10。


    在查询时使用if/case等函数进行条件判断：
    1. if(表达式, tvalue, fvalue)：当表达式为true时，取值tvalue；当表达式为false时，取值fvalue
    2. case  expr  when  value1  then  result1 [when  value2  then  value2 ...] [else  result]  end


    select id,username,gender as 性别数字,if(gender=1,"男","女") as 性别 from tb_emp limit 7,7; -- 起始索引,查询记录数
    id      username        性别数字    性别
    8   	zhouzhiruo  	2       	女
    9   	dingminjun  	2       	女
    10  	zhaomin     	2       	女
    11  	luzhangke   	1       	男
    12  	hebiweng    	1       	男
    13  	fangdongbai 	1       	男
    14  	zhangsanfeng	1       	男

    select
    case
    when job = 1 then "班主任"
    when job = 2 then "讲师"
    when job = 3 then "学工部长"
    when job = 4 then "教研科长"
    when job = null then "未分配职务"
    end as 职务
    ,count(*) as 人数
    from tb_emp
    group by job;
    职务        人数
    nul     	1
    班主任  	5
    讲师    	22
    学工部长	1
    教研科长	1

  --------------------------------------------------------------------------------
    多表关系

    自增约束 auto_increment
    主键约束 primary key --- 主键是一行数据的唯一标识，要求非空且唯一

    外键列:表中记录别的表的主键的字段
    外键关系:a表的一个字段值来自于b表的主键这个字段叫做外键列,这个外键列让两张表产生了外键关系,外键所在表叫子表,依赖的那个主键所在表叫父表.
    多对1的关系:
    当子表有外键关联父表的主键时，就直接形成了多对1的关系。父表的一条数据可以对应子表的多条数据.通常子表是数据多的一方，父表是数据少的一方
    这是一种逻辑上的外键关系,这种关系是人工维护的，不受语法保护

    例如:
    部门数据可以直接删除，然而还有部分员工归属于该部门下，此时就出现了数据的不完整、不一致问题。
    目前上述的两张表，在数据库层面，并未建立关联，所以是无法保证数据的一致性和完整性的。

    通过使用foreign key关键字外键约束可以实现物理外键
    外键约束:语法上定义外键约束后会在语法上保护数据，维护数据表之间的一致性，完整性
    foreign key --- 让两张表的数据建立连接，保证数据的一致性和完整性

    -- 创建表时指定外键
    create table 表名(
        字段名    数据类型,
        ...
        [constraint]   [外键名称]  foreign  key (外键字段名)   references   主表 (字段名)
    );

    -- 建完表后，添加外键
    alter table  表名  add constraint  外键名称  foreign key (外键字段名) references  主表(字段名);

    alter table tb_emp add constraint abcd foreign key(dept_id) references tb_dept(id)

    物理外键
    概念：使用 foreign key 定义外键关联另外一张表。
    缺点：
    影响增、删、改的效率（需要检查外键关系）。
    仅用于单节点数据库，不适用与分布式、集群场景。
    容易引发数据库的死锁问题，消耗性能。

    逻辑外键(尽管逻辑外键并不是真正意义上的外键,表与表之间的关系仅仅靠人工维护,但反而推荐使用逻辑外键,因为它使得删除数据时更加灵活,不会出现要删除一条数据,却要删除多张表的情况)
    概念：在业务层逻辑中，解决外键关联。
    通过逻辑外键，就可以很方便的解决上述问题。


    1对1关系:在任意一张表中定义一个唯一约束的字段作为外键指向于另一张表的主键
    案例: 用户 与 身份证信息 的关系
    关系: 一对一关系，多用于单表拆分，将一张表的基础字段放在一张表中，其他字段放在另一张表中，以提升操作效率
    实现: 在任意一方加入外键，关联另外一方的主键，并且设置外键为唯一的(UNIQUE),将会使得子表中不能出现多条数据对应父表中同一条数据的情况,也即不会出现多对1


    多对多关系:定义一张中间表，至少定义两个字段作为外键分别指向于另外两张表的主键
    案例: 学生 与 课程的关系
    关系: 一个学生可以选修多门课程，一门课程也可以供多个学生选择
    实现: 建立第三张中间表，中间表至少包含两个外键，分别关联两方主键
    例如:
    --  多对多: 学生 与 课程
    create table tb_student(
       id int auto_increment primary key comment '主键ID',
       name varchar(10) comment '姓名',
       no varchar(10) comment '学号'
    ) comment '学生表';
    insert into tb_student(name, no) values ('黛绮丝', '2000100101'),('谢逊', '2000100102'),('殷天正', '2000100103'),('韦一笑', '2000100104');


    create table tb_course(
       id int auto_increment primary key comment '主键ID',
       name varchar(10) comment '课程名称'
    ) comment '课程表';
    insert into tb_course (name) values ('Java'), ('PHP'), ('MySQL') , ('Hadoop');

    -- 逻辑外键
    create table tb_student_course(
        id int auto_increment primary key,
        student_id int,
        course_id int
    )

    -- 物理外键
    create table tb_student_course(
       id int auto_increment comment '主键' primary key,
       student_id int not null comment '学生ID',
       course_id  int not null comment '课程ID',
       constraint fk_courseid foreign key (course_id) references tb_course (id),
       constraint fk_studentid foreign key (student_id) references tb_student (id)
    )comment '学生课程中间表';

    insert into tb_student_course(student_id, course_id) values (1,1),(1,2),(1,3),(2,2),(2,3),(3,4);
--------------------------------------------------------------------------------
    多表查询: 指从多张表中查询数据


     -- 查询所有员工与部门完全组合(笛卡尔积 简单粗暴地将两个表的数据挨个组合展示,存在大量不符合逻辑的错误组合)
    select te.*,td.*
    from  tb_dept as td,tb_emp1 as te;


    查询分类:
    ·连接查询
        ·内连接：相当于查询A、B交集部分数据
        ·外连接
            ·左外连接：查询左表所有数据(包括两张表交集部分数据)
            ·右外连接：查询右表所有数据(包括两张表交集部分数据)
    ·子查询



    连接查询

    内连接：相当于查询A、B交集部分数据
    隐式内连接：select  字段列表   from   表1 , 表2   where   条件 ... ;
    显式内连接：select  字段列表   from   表1  [ inner ]  join 表2  on  连接条件 ... ;

    -- (隐式)内连接查询,查询两表当中有关联的数据
    select te.*,td.*
    from  tb_dept as td,tb_emp1 as te
    where te.dept_id = td.id;

    -- (显示)内连接查询,查询两表当中有关联的数据
    select te.id 员工id,te.username,te.name,te.gender,te.entrydate,te.dept_id,td.id 部门id,td.name
    from  tb_emp1 as te
    inner join tb_dept as td
    on te.dept_id = td.id;

    外连接
        左外连接：查询左表所有数据(包括两张表交集部分数据)
        右外连接：查询右表所有数据(包括两张表交集部分数据)

    左外连接：select  字段列表   from   表1  left  [ outer ]  join 表2  on 连接条件 ... ;
    右外连接：select  字段列表   from   表1  right  [ outer ]  join 表2  on  连接条件 ... ;

    -- 外连接,使用(left/right) outer join ,必须要指定左右,指定左时,左表数据无论是否有关联全部显示,右表亦之
    -- 左外连接
    select te.id 员工id,te.username,te.name,te.gender,te.entrydate,te.dept_id,td.id 部门id,td.name
    from  tb_emp1 as te  left outer join tb_dept as td on te.dept_id = td.id;
    -- 右外连接
    select te.id 员工id,te.username,te.name,te.gender,te.entrydate,te.dept_id,td.id 部门id,td.name
    from  tb_emp1 as te  right outer join tb_dept as td on te.dept_id = td.id
    order by td.id;


    子查询
    介绍：SQL语句中嵌套select语句，称为嵌套查询，又称子查询。
    形式：select  *  from   t1   where  column1 =  ( select  column1  from  t2  … );
    子查询外部的语句可以是insert / update / delete / select 的任何一个，最常见的是 select。

   ·值子查询：子查询返回的结果为单个值
    子查询语句结果是单行单列，子查询语句作为条件值，使用 = != > < 等进行条件判断

    -- 子查询结果是一个数
    -- 查询id大于平均id的数据
    select avg(id) from tb_emp1
    select * from tb_emp1 as te where id >= (select avg(id) from tb_emp1)

    ·列子查询：子查询返回的结果为一列
    子查询语句结果是多行单列，子查询语句作为条件值，使用 in 等关键字进行条件判断

    -- 子查询结果是一列数据
    -- 查询入职时间在2000-01-01到2010-12-31之间的员工
    select id from tb_emp1 where entrydate BETWEEN "2000-01-01" and "2010-12-31"
    select * from tb_emp1 where id in(select id from tb_emp1 where entrydate BETWEEN "2000-01-01" and "2010-12-31")

    ·表子查询：子查询返回的结果为多行多列
    子查询返回的结果是多行多列，常作为临时表,常用的操作符：from

    -- 子查询结果是一张新的表
    -- 查询入职时间在2000-01-01到2010-12-31之间的所有不含"张"的员工
    select * from tb_emp1 where name not like "%张%"
    select * from (select * from tb_emp1 where name not like "%张%") as newTable where id in(select id from tb_emp1 where entrydate BETWEEN "2000-01-01" and "2010-12-31")

  --------------------------------------------------------------------------------
    事务Transaction
    事务 是一组操作的集合，它是一个不可分割的工作单位。事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求，即这些操作 要么同时成功，要么同时失败。
    默认MySQL的事务是自动提交的，也就是说，当执行一条DML语句，MySQL会立即隐式的提交事务。

    -- 删除6号部门及其部门下的所有人
    start transaction;/begin ; -- 开启事务
    try {
        delete from tb_dept where id = 6;
        select * from tb_dept;
        delete from tb_emp where dept_id =6;
        commit; -- 提交
    }catch() {
        rollback; -- 回滚事务
    }


    事务的4大特性(ACID)

    Atomicity 原子性
    事务是不可分割的最小单元，要么全部成功，要么全部失败
    原子性是指事务包含的所有操作要么全部成功，要么全部失败回滚。
    因此事务的操作如果成功就必须要完全应用到数据库，如果操作失败则不能对数据库有任何影响。

    Consistency 一致性
    事务完成时，必须使所有的数据都保持一致状态
    一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态。
    也就是说一个事务执行之前和执行之后都必须处于一致性状态。

    Isolation (事务之间的)隔离性
    数据库系统提供的隔离机制，保证事务在不受外部并发操作影响的独立环境下运行,也即只有事务内部可以看到数据的变化,事务外看不到这些提交前的临时变化
    隔离性是当多个用户并发访问数据库时，比如操作同一张表时，数据库为每一个用户开启的事务。
    不能被其他事务的操作所干扰，多个并发事务之间要相互隔离。


    Durability 持久性
    事务一旦提交或回滚，它对数据库中的数据的改变就是永久的
     持久性是指一个事务一旦被提交了，那么对数据库中的数据的改变就是永久性的，即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。


    数据库存在4种隔离级别:
    隔离级别                名称                会引发的问题
    read uncommitted        读未提交            脏读,不可重复,幻读
    read committed          读已提交            不可重复读,幻读
    repeatable read         可重复读            幻读
    serializable            串行化              无


    问题          现象
    脏读          在一个事务处理过程中读取到了另一个未提交事务中的数据 , 导致两次查询结果不一致
    不可重复读    在一个事务处理过程中读取到了另一个事务中修改并已提交的数据, 导致两次查询结果不一致
    幻读          查询某数据不存在 准备插入此记录，但执行插入时发现此记录已存在，无法插入。
                  或查询数据不存在执行删除操作，却发现删除成功。

 --------------------------------------------------------------------------------
    索引
    索引（index）是帮助数据库 高效获取数据 的 数据结构,是一张新的表,MySQL数据库中默认的索底层结构是B+tree(多路平衡搜索树)结构

    注意: 主键字段，在建表时，会自动创建主键索引。
          添加唯一约束时，数据库实际上会添加唯一索引。

    手动创建索引
    create  [ unique ]  index 索引名 on  表名 (字段名,... ) ;
    查看索引
    show  index  from  表名;
    删除索引
    drop  index  索引名  on  表名;

    优点: 提高数据查询的效率，降低数据库的IO成本。
          通过索引列对数据进行排序，降低数据排序的成本，降低CPU消耗。

    缺点: 索引会占用存储空间。
          索引大大提高了查询效率，同时却也降低了insert、update、delete的效率。


    测试数据库有无索引性能数据准备(400 0000 条数据)
    # 创建数据库
    create database day03;
    # 使用数据库
    use day03;
    -- 1. 准备表
    CREATE TABLE `user`(
        id INT,
        username VARCHAR(32),
        `password` VARCHAR(32),
        sex VARCHAR(6),
        email VARCHAR(50)
    );

    -- 2. 创建存储过程，实现批量插入记录
    DELIMITER $$ -- 声明存储过程的结束符号为$$
    CREATE PROCEDURE auto_insert()
    BEGIN
        DECLARE i INT DEFAULT 1;
        START TRANSACTION; -- 开启事务
        WHILE(i<=4000000)DO
            INSERT INTO `user` VALUES(i,CONCAT('jack',i),MD5(i),'male',CONCAT('jack',i,'@itcast.cn'));
            SET i=i+1;
        END WHILE;
        COMMIT; -- 提交
    END$$ -- 声明结束
    DELIMITER ; -- 重新声明分号为结束符号

    -- 3. 查看存储过程
    SHOW CREATE PROCEDURE auto_insert;

    -- 4. 调用存储过程
    CALL auto_insert();

    -- 先不把id设为主键,执行以下语句,再把id设为主键,再次执行以下语句,观察搜索时间差别
    select * from user where id = 251234;

  ·从网页原型抽象出数据库表时通常观察两点:"列表页"和"表单页"

  --------------------------------------------------------------------------------

*/
//三表内连接
//SELECT * FROM table1
//JOIN table2 JOIN table3
//ON table1.column1 = table2.column2
//AND table2.column3 = table3.column4
//与
//SELECT * FROM table1
// JOIN table2
//ON table1.column1 = table2.column2
//JOIN table3
// ON table2.column3 = table3.column4
//有什么不同吗
//这两个 SQL 查询语句实现的功能是一样的，都是从 table1、table2 和 table3 进行内连接，连接条件为 table1.column1 = table2.column2 和 table2.column3 = table3.column4。两个查询语句返回的结果集也是一样的。
//不同之处在于这两个查询语句的写法不同。第一个查询语句将 JOIN 关键字放在了 table2 和 table3 之间，因此数据库可能会根据自己的优化算法来决定连接顺序；而第二个查询语句将 JOIN 关键字放在了连接条件之后，明确指定了连接顺序。
//在实际应用中，两种写法都可以使用，但是为了保证代码的可读性和稳定性，建议尽量采用第二种写法，即将 JOIN 关键字放在连接条件之后，明确指定连接顺序。这样可以使代码更加清晰明了，减少歧义和错误，便于后续维护和优化。


/**
 *
 --------------------------------------------------------------------------------
 <<Mybatis笔记>>
 --------------------------------------------------------------------------------

 什么是Mybatis?
 MyBatis是一款优秀的底层为JDK中的JDBC(Java Database Connectivity)的持久层(DAO)框架,用于简化JDBC的开发。

 MyBatis本是 Apache的一个开源项目iBatis, 2010年这个项目由apache迁移到了google code，并且改名为MyBatis 。2013年11月迁移到Github。

 ----------------------------------------------------------

 使用Mybatis查询所有用户数据的步骤:

  1.准备工作---创建SpringBoot模块,创建数据库表user,在domain/pojo/entity(通常用于存储实体类的包都以三者之一命名)包下创建实体类User,实体类名对应于数据库表名

    public class User {
        private Integer id; //成员变量与表中字段内容一致,普通类型要使用包装类
        private String name;
        private Integer age;
        private Integer gender;
        private String phone;

        getter
        setter
        toString
    }

  2.引入Mybatis相关依赖(可以在创建SpringBoot模块时就选择添加,将无需再手动添加)

  在pom.xml中<dependencies> ...</dependencies>添加依赖
   <!--Mybatis Framework-->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.3.0</version>
    </dependency>
    <!--Mysql driver-->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>

  ,并在resources资源文件夹下编写MyBatis配置文件application.properties用于指定数据库连接信息:数据库驱动,数据库连接的url,数据库用户名,对应的密码

    #驱动类名称
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    #数据库连接的url
    spring.datasource.url=jdbc:mysql://localhost:3306/learnsql  (此处的learnsql为要连接的数据库名,3306为MySQL默认端口号)
    #连接数据库的用户名
    spring.datasource.username=root
    #连接数据库的密码
    spring.datasource.password=root

  3.在dao包下创建UserMapper(一个方法映射一个SQL语句,User实体类映射user表中一行),在注解中编写SQL语句(注解形式/XML形式)

    @Mapper //该注解表示该函数用于映射数据库中的表,以及将该接口的管理权控制反转交给Spring的容器管理,在需要该接口实现类对象的地方使用@Autowired注解依赖注入
    public interface UserMapper { //注意是接口不是类
        @Select("select * from user") //注解中编写SQL语句
        public abstract List<User> list(); //将来调用此方法将返回一个List<User>当中的每一个User就是数据库中user表里的一行数据封装成的对象,这整个过程都是由Mybatis完成的
    }

  4.在src/test/java下的测试类中进行单元测试 或 直接主函数中测试

    @SpringBootTest //专门的测试类
    class MybatisDemoApplicationTests {
        //注入mapper
        @Autowired //依赖注入,Mybatis框架内部用反射的方式定义了该接口的实现类,并创建对象,但由于是Springboot项目,对象管理交由Spring完成,Spring会将IOC容器中管理的@Mapper接口的实现类对象赋值给userMapper
        private UserMapper userMapper;

        @Test
        public void testUserMapper() {
            //使用mapper
            List<User> list = userMapper.list(); //Mybatis已经给出了list()的实现,将会直接返回user表中每行数据封装成的对象列表
            System.out.println(list);
            System.out.println(list);

            //[User{id=1, name='白眉鹰王', age=55, gender=1, phone='18800000000'}, User{id=2, name='金毛狮王', age=45, gender=1, phone='18800000001'}, User{id=3, name='青翼蝠王', age=38, gender=1, phone='18800000002'}, User{id=4, name='紫衫龙王', age=42, gender=2, phone='18800000003'}, User{id=5, name='光明左使', age=37, gender=1, phone='18800000004'}, User{id=6, name='光明右使', age=48, gender=1, phone='18800000005'}]
            }
        }

----------------------------------------------------------


    配置SQL提示:
    默认在mybatis中编写SQL语句是不识别的。可以做如下配置：
    1.SQL语句上 alt + enter
    2.Show Context Actions
    3.Inject language or reference
    4.MySQL(SQL)
    但是需要在Idea中配置自带的MySQL数据库并连接才能提示SQL语句错误

----------------------------------------------------------

    介绍JDBC
    JDBC： ( Java DataBase Connectivity )，是Sun公司提供的一套操作关系型数据库的Java语言API
    sun公司官方定义的一套操作所有关系型数据库的规范，即接口。
    各个数据库厂商去实现这套接口，提供数据库驱动jar包。
    我们可以使用这套接口（JDBC）编程，真正执行的代码是驱动jar包中的实现类。

----------------------------------------------------------

    数据库连接池(MyBatis使用了数据库连接池,效率优于JDBC)

    数据库连接池是个容器，负责分配、管理数据库连接(Connection)
    它允许应用程序重复使用一个现有的数据库连接，而不是再重新建立一个
    释放空闲时间超过最大空闲时间的连接，来避免因为没有释放连接而引起的数据库连接遗漏

    使用数据库连接池的好处是:
    资源重用
    提升系统响应速度
    避免数据库连接遗漏

    常见数据库连接池:
    C3P0 DBCP(没有使用SpringBoot单独使用Mybatis时默认) Druid德鲁伊 Hikari(SpringBoot默认)
    Druid（德鲁伊）
    Druid连接池是阿里巴巴开源的数据库连接池项目
    功能强大，性能优秀，是Java语言最好的数据库连接池之一

    使用德鲁伊连接池步骤:

    pom.xml引入依赖jar包
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.2.8</version>
    </dependency>

    使用的德鲁伊数据库连接池配置文件
    spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.druid.url=jdbc:mysql://localhost:3306/learnsql
    spring.datasource.druid.username=root
    spring.datasource.druid.password=root

----------------------------------------------------------

    Lombok(小辣椒)是一个实用的Java类库，可以通过简单的注解来简化和消除一些必须有但显得很臃肿的Java代码。
    通过注解的形式自动生成构造器、getter/setter、equals、hashcode、toString等方法，并可以自动化生成日志变量，简化java开发、提高效率。
    注解                    作用
    @Getter/@Setter         为所有的属性提供get/set方法
    @ToString               会给类自动生成易阅读的  toString 方法
    @EqualsAndHashCode      根据类所拥有的非静态字段自动重写 equals 方法和
                            hashCode 方法
    @Data                   提供了更综合的生成代码功能（@Getter  + @Setter +
                            @ToString + @EqualsAndHashCode）
    @NoArgsConstructor      为实体类生成无参的构造器方法
    @AllArgsConstructor     为实体类生成除了static修饰的字段之外带有各参数的构造器
                            方法。

    使用步骤:
    1.在pom.xml文件中引入引入依赖
    <!-- 在springboot的父工程中，已经集成了lombok并指定了版本号，故当前引入依赖时不需要指定version -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>

    2.第2步：在实体类上添加注解
    import lombok.Data;
    @Data
    public class User {
        private Integer id;
        private String name;
        private Short age;
        private Short gender;
        private String phone;
    }
    在实体类上添加了@Data注解，那么这个类在编译时期，就会生成getter/setter、equals、hashcode、toString等方法。
    说明：@Data注解中不包含全参构造方法，通常在实体类上，还会添加上：全参构造、无参构造
    import lombok.Data;
    @Data //getter方法、setter方法、toString方法、hashCode方法、equals方法
    @NoArgsConstructor //无参构造
    @AllArgsConstructor//全参构造
    public class User {
        private Integer id;
        private String name;
        private Short age;
        private Short gender;
        private String phone;
    }


    Lombok的注意事项：
    - Lombok会在编译时，会自动生成对应的java代码
    - 在使用lombok时，还需要安装一个lombok的插件（新版本的IDEA中自带）

----------------------------------------------------------------------------------
    ##
 */
}
